from PySide2 import QtCore, QtGui, QtWidgets

dict_of_dicts={
    'dict1':{'k1':'v1', 'k2':'v2', 'k3':'v3', 'EXISTING_DICT': 'dict3'},
    'dict2':{'k4':'v4', 'EXISTING_DICT': 'dict1'},
    'dict3':{'k5':'v5', 'k6':'v6', 'k7':'v7', 'EXISTING_DICT': 'dict4'},
    'dict4':{'k8':'v8', 'EXISTING_DICT':'dict2'},
}

def create_model_from_dict(d, parent=None):
    model = QtGui.QStandardItemModel(0, 2, parent)
    for k, v in dict_of_dicts.items():
        it = QtGui.QStandardItem(k)
        model.appendRow(it)
        for k_, v_ in v.items():
            if k_ != "EXISTING_DICT":
                it.appendRow([QtGui.QStandardItem(k_), QtGui.QStandardItem(v_)])
            else:
                child_it = QtGui.QStandardItem(v_)
                child_it.setTextAlignment(QtCore.Qt.AlignCenter)
                child_it.setData(True, QtCore.Qt.UserRole + 1000)
                it.appendRow(child_it)
    return model

class TableView(QtWidgets.QTableView):
    leftDoubleClicked = QtCore.Signal(QtCore.QModelIndex)

    def mouseDoubleClickEvent(self, event):
        super(TableView, self).mouseDoubleClickEvent(event)
        if event.buttons() & QtCore.Qt.LeftButton:
            ix = self.indexAt(event.pos())
            if ix.isValid(): self.leftDoubleClicked.emit(ix)

class Widget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(Widget, self).__init__(parent)
        model = create_model_from_dict(dict_of_dicts, self)

        self.tableview = TableView()
        self.tableview.setModel(model)
        self.tableview.leftDoubleClicked.connect(self.handleSelectionChangedTV)

        self.listview = QtWidgets.QListView()
        self.listview.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection)
        self.listview.setModel(model)
        self.listview.selectionModel().selectionChanged.connect(self.handleSelectionChangedLV)
        self.listview.selectionModel().select(model.index(0, 0), QtCore.QItemSelectionModel.Select)

        hlay = QtWidgets.QHBoxLayout(self)
        hlay.addWidget(self.listview)
        hlay.addWidget(self.tableview)

    @QtCore.Slot(QtCore.QItemSelection)
    def handleSelectionChangedLV(self, item):
        ixs = item.indexes()
        if ixs:
            self.tableview.setRootIndex(ixs[0])
            model = self.tableview.model()
            self.tableview.clearSpans()
            for r in range(model.rowCount(self.tableview.rootIndex())):
                index = model.index(r, 0, self.tableview.rootIndex())
                if index.data(QtCore.Qt.UserRole + 1000):
                    self.tableview.setSpan(r, 0, 1, 2)

    @QtCore.Slot(QtCore.QModelIndex)
    def handleSelectionChangedTV(self, ix):
        if ix.data(QtCore.Qt.UserRole + 1000):
            items = self.listview.model().findItems(ix.data())
            if items:
                self.tableview.clearSelection()
                self.listview.selectionModel().select(items[0].index(), QtCore.QItemSelectionModel.ClearAndSelect)

if __name__ == '__main__':
    import sys
    app = QtWidgets.QApplication(sys.argv)
    w = Widget()
    w.show()
    sys.exit(app.exec_())